#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>

#include <util/msg.h>
#include <util/math_macros.h>
#include <util/list.h>


#include "../image.h"
#include "../../geometry/point.h"
#include "../../geometry/segment.h"
#include "../../geometry/triangle.h"

#include "_draw_primitives.h"

#include "draw.h"

#define adjust( a, min, max ) a = ((a) < min) ? min : ((a)>=max) ? (max-1) : (a);

#define swap( a, b, type ) { type tmp; tmp = (a); (a) = (b); (b) = tmp; }
#define round( val ) ( ( (val)-(int)(val) ) ) > 0.5 ? (int)val+1 : (int)val
#define abs( val ) (val)<0?-(val):(val)

static Color __fg_color;
static Color __bg_color;
static Font __font_;

static ImpImage * __img;

static inline void _set_image(ImpImage * img) {
	__img = img;
	_set_array( img->data, img->width, img->height, img->width * img->nchannels );
}

static void _draw_print_text(ImpImage * img, int row, int col, char fill_ground, char * imp_image_label);


void imp_imdraw_set_color(Color cor) {
	__fg_color = cor;
	_set_color(cor.px);
}

void imp_imdraw_set_fg_color(Color cor) {
	__fg_color = cor;
}

void imp_imdraw_set_bg_color(Color cor) {
	__bg_color = cor;
}

/* Primitives =============================================================== */
inline void imp_imdraw_put_pixel(ImpImage * img, int x, int y) {
	if (x < 0 || x >= img->width) return;
	if (y < 0 || y >= img->height) return;
	//img->methods.set_pixel(img, x, y, __fg_color.px);
	_put_pixel(x,y);
}

void imp_imdraw_line(ImpImage * img, int x0, int y0, int x1, int y1) {

	_set_image(img);
	_bresenham_line(x0, y0, x1, y1);
	

}

void imp_imdraw_square(ImpImage * img, int x, int y, int ray) {
	int x1, x2, y1, y2;

	x1 = x - ray;
	x2 = x + ray;
	y1 = y - ray;
	y2 = y + ray;

	_set_image(img);

	_bresenham_line(x1, y1, x2, y1);
	_bresenham_line(x1, y1, x1, y2);
	_bresenham_line(x2, y2, x2, y1);
	_bresenham_line(x2, y2, x1, y2);
}

void imp_imdraw_cross(ImpImage * img, int x, int y, int ray) {
	_set_image(img);
	_bresenham_line(x - ray, y, x + ray, y);
	_bresenham_line(x, y - ray, x, y + ray);

}

void imp_imdraw_rect(ImpImage * img, int x0, int y0, int x1, int y1) {

	_set_image(img);
	/* horizontal */
	_bresenham_line(x0, y0, x1, y0);
	_bresenham_line(x0, y1, x1, y1);
	/* vertical */
	_bresenham_line(x0, y0, x0, y1);
	_bresenham_line(x1, y0, x1, y1);
}

void imp_imdraw_circle(ImpImage * img, int xm, int ym, int ray) {

	_set_image(img);
	_bresenham_circle(xm, ym, ray);

}

void imp_imdraw_ellipse(ImpImage * img, int xc, int yc, int a, int b, real deg) {

	_set_image(img);
	_ellipse(xc, yc, a, b, deg);

}

void imp_imdraw_poly(ImpImage * img, Ref2 * p, int n) {
	Ref2 p1, p2;
	int i;

	_set_image(img);

	for (i = 0; i < n; i++) {
		p1 = p[i];
		p2 = p[ (i + 1) == n ? 0 : (i + 1)];

		_bresenham_line(p1.x, p1.y, p2.x, p2.y);

	}
}

void imp_imdraw_poly_fill(ImpImage * img, Ref2 * p, int n) {

	_set_image(img);
	_scan_line_poly_fill_fast(p, n);

}

/* Fonts ==================================================================== */
void imp_imdraw_set_font(Font f) {
	__font_ = f;
}

static void _draw_print_text(ImpImage * img, int col, int row, char fill_ground, char * imp_image_label) {

	int i, x, y, f, len;
	int idx;
	int w, h;

	int width;
	int height;
	unsigned char * image;

	__font_ = FONT_6x11;

	width = img->width;
	height = img->height;
	image = img->data;

	len = strlen(imp_image_label);


	for (y = 0; y < __font_.char_height; y++) {
		h = row + y;
		if (h >= height || h < 0) continue;

		for (x = 0; x < len; x++) {

			f = __font_.fontdata[imp_image_label[x] * __font_.char_height + y];

			for (i = 0; i < __font_.char_width; i++) {

				w = col + (__font_.char_width * x) + i;

				if (w >= width || w < 0) continue;

				if (f & (__font_.mask >> i)) {

					imp_imdraw_put_pixel(img, w, h);
					/*
						idx = (h * width + w)*3;
						image[idx] = __fg_color.rgb.r;
						image[idx + 1] = __fg_color.rgb.g;
						image[idx + 2] = __fg_color.rgb.b;
					 */

				} else {
					if (fill_ground) {

						imp_imdraw_put_pixel(img, w, h);
						idx = (h * width + w)*3;
						image[idx] = __bg_color.rgb.r;
						image[idx + 1] = __bg_color.rgb.g;
						image[idx + 2] = __bg_color.rgb.b;
					}

				}
			}

		}
	}
}

void imp_imdraw_print_text(ImpImage * img, int x, int y, int fillbg, char * template, ...) {

	char msg[256];
	va_list ap;

	va_start(ap, template);
	vsprintf(msg, template, ap);
	va_end(ap);

	_draw_print_text(img, x, y, fillbg, msg);
}

/*============================================================================*/


void imp_image_draw_point(ImpImage * img, ImpPoint * p) {

	imp_imdraw_circle(img, p->x, p->y, 2);
}

void imp_image_draw_segment(ImpImage * img, ImpSegment * s) {

	ImpPoint * p1;
	ImpPoint * p2;

	p1 = IMP_POINT(s->v[0]);
	p2 = IMP_POINT(s->v[1]);

	imp_imdraw_line(img, p1->x, p1->y, p2->x, p2->y);
}

void imp_image_draw_triangle(ImpImage * img, ImpTriangle * t) {

	ImpPoint p;
	ImpPoint pm;
	imp_image_draw_segment(img, IMP_SEGMENT(t->e[0]));
	imp_image_draw_segment(img, IMP_SEGMENT(t->e[1]));
	imp_image_draw_segment(img, IMP_SEGMENT(t->e[2]));

/*
	p = imp_triangle_barycenter(t);
	imp_image_draw_point(img, &p);

	ImpVertex * v1, * v2, * v3;

	imp_triangle_extract_vertices(t, &v1, &v2, &v3);

	pm = imp_point_mid_point(&p, IMP_POINT(v1));
	imp_imdraw_put_pixel(img, pm.x, pm.y);
	pm = imp_point_mid_point(&p, IMP_POINT(v2));
	imp_imdraw_put_pixel(img, pm.x, pm.y);
	pm = imp_point_mid_point(&p, IMP_POINT(v3));
	imp_imdraw_put_pixel(img, pm.x, pm.y);
*/

}

void imp_image_draw_vertices(ImpImage * img, List * vertices) {

	ListIterator * it;
	ImpPoint * p;

	it = list_get_first(vertices);
	while (it) {

		imp_image_draw_point(img, IMP_POINT(it->data));
		ImpPoint * p = IMP_POINT(it->data);
		//imp_imdraw_print_text(img, p->x+3, p->y+3, 0, "%d", (int) IMP_VERTEX(p)->id);

		it = it->next;
	}
}

void imp_image_draw_edges(ImpImage * img, List * edges) {

	ListIterator * it;

	it = list_get_first(edges);
	while (it) {

		imp_image_draw_segment(img, IMP_SEGMENT(it->data));
		it = it->next;
	}
}

void imp_image_draw_triangles(ImpImage * img, List * triangles) {


	ListIterator * it;
	ImpTriangle * t;
	
	_return_if_fail(img && triangles);


	it = list_get_first(triangles);
	while (it) {
		t = IMP_TRIANGLE(it->data);

		imp_image_draw_triangle(img, t);

		it = it->next;
	}

}

